package com.ls.mybatis.core;

import java.sql.*;
import java.util.Enumeration;
import java.util.Vector;

/**
 * 数据库连接池
 */
public class ConnectionPool {
    //数据库驱动类
    private String dirverClass = "com.mysql.cj.jdbc.Driver";
    //数据库连接地址
    private String url = "jdbc:mysql://127.0.0.1:3306/stu?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useOldAliasMetadataBehavior=true";
    //用户名字
    private String username = "root";
    //用户密码
    private String password = "123456";

    /**
     * 默认的连接池参数
     */
    //初始化连接数量
    private int initConnections = 10;
    //最大连接数量
    private int maxConnection = 50;
    //连接池中连接数量不够时自动递增数量
    private int initcrementConnection = 5;
    //每一个连接的存放容器
    private Vector<PooledConnection> connections = null;

    /**
     * 无参构造
     */
    public ConnectionPool() {
    }

    /**
     * 提供数据库连接信息的构造方法
     * @param dirverClass
     * @param url
     * @param username
     * @param password
     */
    public ConnectionPool(String dirverClass, String url, String username, String password) {
        this.dirverClass = dirverClass;
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public int getInitConnections() {
        return initConnections;
    }

    public void setInitConnections(int initConnections) {
        this.initConnections = initConnections;
    }

    public int getMaxConnection() {
        return maxConnection;
    }

    public void setMaxConnection(int maxConnection) {
        this.maxConnection = maxConnection;
    }

    public int getInitcrementConnection() {
        return initcrementConnection;
    }

    public void setInitcrementConnection(int initcrementConnection) {
        this.initcrementConnection = initcrementConnection;
    }

    public Vector<PooledConnection> getConnections() {
        return connections;
    }

    public void setConnections(Vector<PooledConnection> connections) {
        this.connections = connections;
    }

    /**
     * 连接池中的连接对象
     */
    class PooledConnection{
        private Connection connection = null; //数据库连接对象
        private boolean busy = false; //默认空闲
        private int pid; //连接号

        public PooledConnection() {
        }

        public PooledConnection(Connection connection, int pid) {
            this.connection = connection;
            this.pid = pid;
        }

        @Override
        public String toString() {
            return "PooledConnection{" +
                    "connection=" + connection +
                    ", busy=" + busy +
                    ", pid=" + pid +
                    '}';
        }

        public Connection getConnection() {
            return connection;
        }

        public void setConnection(Connection connection) {
            this.connection = connection;
        }

        public boolean isBusy() {
            return busy;
        }

        public void setBusy(boolean busy) {
            this.busy = busy;
        }

        public int getPid() {
            return pid;
        }

        public void setPid(int pid) {
            this.pid = pid;
        }
    }

    /**
     * 返回连接池中的连接对象的个数
     */
    public int getActiveConnectionSize(){
        return connections.size();
    }

    /**
     * 创建连接池
     */
    public synchronized void createPool() throws Exception {
        //判断是否创建
        if( connections != null){
            return;
        }

        Driver driver = (Driver)Class.forName(this.dirverClass).newInstance();
        //注册驱动
        DriverManager.registerDriver(driver);
        //创建连接池对象容器
        connections = new Vector<PooledConnection>();
        //将连接池对象放入容器中，其中封装了数据库连接
        createConnections(this.initConnections);
        System.out.println("数据库连接池已经创建....");
    }

    /**
     * 将连接池对象放入容器中，将连接池对象放入容器中，其中封装了数据库连接
     * @param numConnections
     */
    private void createConnections(int numConnections) throws SQLException {
        for( int i = 0; i < numConnections; i++){
            //判断数据库连接数是否已经到达上限
            if(this.maxConnection > 0 && this.connections.size() >= this.maxConnection){
                System.out.println("连接池已达上限");
                return;
            }
            //将连接池对象放入容器中，其中封装库数据库连接对象
            connections.addElement(new PooledConnection(newConnection(), i + 1));
        }
    }

    /**
     * 创建数据库连接
     * @return
     * @throws SQLException
     */
    private synchronized Connection newConnection() throws SQLException {
        //获取一个数据库连接
        Connection con = DriverManager.getConnection(url, username, password);
        //如果是第一次创建，则需要检验数据库能支持的最大连接数
        if(connections.size() == 0){
            //获取数据库原始数据
            DatabaseMetaData metaData = con.getMetaData();
            // 数据库可以支持的最大连接数
            int driverMaxConnections = metaData.getMaxConnections();
            //如果用户设置的最大连接数超过了数据库可以支持的连接数则将数据库连接数设置为最大值
            if(driverMaxConnections > 0 && driverMaxConnections < this.maxConnection){
                this.maxConnection = driverMaxConnections;
            }
        }
        return con;
    }

    /**
     * 获取一个可用连接池对象中的连接
     * @return
     */
    public synchronized Connection getConnection() throws SQLException {
        if(connections == null){
            return null;
        }
        //获取一个空闲的连接池对象中的连接
        Connection con = getFreeConnection();
        //如果没有空闲的连接等待一秒钟
        while(con == null){
            wait(1000);
            con = getFreeConnection();
        }
        return con;
    }

    /**
     * 获取空闲连接方法
     * @return
     */
    private Connection getFreeConnection() throws SQLException {
        Connection con = findFreeConnection();
        if(con == null){ //说明没有空闲连接
            createConnections(initcrementConnection);  //先扩容
            con = findFreeConnection();
            if(con == null){
                return null;
            }
        }
        return con;
    }

    /**
     * 查找连接池连接对象中的空闲连接
     * @return
     */
    private Connection findFreeConnection() throws SQLException {
        Connection con  = null;
        PooledConnection poolCon;
        Enumeration<PooledConnection> enumeration = connections.elements();
        while(enumeration.hasMoreElements()){ //循环容器中的连接池对象
            poolCon = enumeration.nextElement(); //获取连接池中的连接对象
            if(!poolCon.isBusy()){ //如果是空闲的
                con = poolCon.getConnection(); //获取数据库连接对象中的连接
                poolCon.setBusy(true);  //将连接池中的连接对象状态修改为忙碌
                if(!testConnection(con)){ //测试连接是否可以用
                    con = newConnection(); //不可用则获取一个新的连接
                    poolCon.setConnection(con); //将其替换
                }
                System.out.println(poolCon.getPid() + "号连接池对象开始捉妖啦！！！！！");
                break;
            }
        }
        return con;
    }

    /**
     *测试连接可不可用
     */
    private boolean testConnection(Connection con) {
        try {
            //自动提交事务为true
            con.setAutoCommit(true);
        } catch (SQLException e) { //出异常则关闭这个连接
            colseConnection(con);
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 关闭连接
     * @param con
     */
    private void colseConnection(Connection con) {
        if(con != null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 释放连接,将连接池连接对象中的连接修改为不忙碌
     * 就算是将其归还到连接对象容器中了
     */
    public void freeConnection(Connection con){
        if(con == null){
            return;
        }
        PooledConnection poolcon ;
        Enumeration<PooledConnection> enumeration = connections.elements();
        while (enumeration.hasMoreElements()){
            poolcon = enumeration.nextElement();
            if(con == poolcon.getConnection()){
                poolcon.setBusy(false);
                return;
            }
        }
    }

    /**
     * 关闭连接池，将连接池中的所有连接关闭，移除容器中的连接池连接对象
     * 就算是关闭连接池了
     */
    public synchronized void colseConnectionPool(){
        if(connections == null){
            return;
        }
        PooledConnection poolcon = null;
        Enumeration<PooledConnection> enumeration = connections.elements();
        while (enumeration.hasMoreElements()){
            poolcon = enumeration.nextElement(); //获取连接对象
            if(poolcon.isBusy()){
                //等待5秒钟
                wait(5000);
            }
            //将连接池对象中的连接关闭
            colseConnection(poolcon.getConnection());
            connections.removeElement(poolcon);
        }
        connections = null;
    }

    /**
     * 指定等待时间
     * @param mseconds
     */
    private void wait(int mseconds){
        try {
            Thread.sleep(mseconds);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
